﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using Autofac;
using Autofac.Core;
using Cloak.Mvp.Autofac.ViewParameters;
using Cloak.Reflection;

namespace Cloak.Mvp.Autofac
{
	/// <summary>
	/// A context which governs the lifetime of presenter instances
	/// </summary>
	public sealed class PresenterContext : IPresenterContext
	{
		private readonly IComponentContext _componentContext;
		private readonly IViewParameterSpecification _viewParameterSpecification;

		/// <summary>
		/// Initializes a context with the specified component context and specification for determining view parameters
		/// </summary>
		/// <param name="componentContext">The context in which presenters are resolved</param>
		/// <param name="viewParameterSpecification">
		/// The specification which determines the view parameters of presenter constructors
		/// </param>
		public PresenterContext(IComponentContext componentContext, IViewParameterSpecification viewParameterSpecification)
		{
			Contract.Requires(componentContext != null);
			Contract.Requires(viewParameterSpecification != null);

			_componentContext = componentContext;
			_viewParameterSpecification = viewParameterSpecification;
		}

		#region IPresenterContext
		/// <summary>
		/// Creates a presenter to take effect at the specified usage site
		/// </summary>
		/// <param name="site">The usage of the presenter</param>
		/// <returns>The presenter instance</returns>
		public object CreatePresenter(PresenterSite site)
		{
			if(site is NamedPresenterSite)
			{
				return CreateNamedPresenter((NamedPresenterSite) site);
			}
			else if(site is KeyedPresenterSite)
			{
				return CreateKeyedPresenter((KeyedPresenterSite) site);
			}
			else
			{
				return CreateBasicPresenter(site);
			}
		}
		#endregion

		private object CreateBasicPresenter(PresenterSite site)
		{
			return _componentContext.Resolve(site.PresenterType, CreateViewParameter(site));
		}

		private object CreateNamedPresenter(NamedPresenterSite site)
		{
			return _componentContext.ResolveNamed(site.Name, site.PresenterType, CreateViewParameter(site));
		}

		private object CreateKeyedPresenter(KeyedPresenterSite site)
		{
			return Invoke.Func<object>(this, "CreateKeyedPresenter", new[] { site.PresenterType }, site);
		}

		private object CreatedKeyedPresenter<T>(KeyedPresenterSite site)
		{
			return _componentContext.ResolveKeyed<T>(site.Key, CreateViewParameter(site));
		}

		private Parameter CreateViewParameter(PresenterSite site)
		{
			return new ViewParameter(site.View, _viewParameterSpecification);
		}
	}
}